#ifndef _SOCKETIMPL_HPP_
#define _SOCKETIMPL_HPP_

#include "types.hpp"
#include "jhdefs.hpp"
#include "net/SocketDefs.hpp"
#include "net/SocketAddress.hpp"
#include "thread/atomiccounter.hpp"
using JHCPP::thread::CRefCountedObject;
#include "stdlib/timespan.hpp"
using JHCPP::stdlib::CTimeSpan;

NET_NAMESPACE_BEGIN

class CSocketImpl : public CRefCountedObject
{
public:
	enum SelectMode
	{
		SELECT_READ  = 1,
		SELECT_WRITE = 2,
		SELECT_ERROR = 4
	};

	virtual CSocketImpl* acceptConnection(CSocketAddress& clientAddr);
	virtual void connect(const CSocketAddress& address);
	virtual void connect(const CSocketAddress& address, const CTimeSpan& timeout);
	virtual void connectNB(const CSocketAddress& address);

	virtual void bind(const CSocketAddress& address, bool reuseAddress = false);
	virtual void bind6(const CSocketAddress& address, bool reuseAddress = false, bool ipV6Only = false);

	virtual void listen(int backlog = 64);
	virtual void close();

	virtual void shutdownReceive();
	virtual void shutdownSend();
	virtual void shutdown();

	virtual int sendBytes(const void* buffer, int length, int flags = 0);
	virtual int receiveBytes(void* buffer, int length, int flags = 0);
	virtual int sendTo(const void* buffer, int length, const CSocketAddress& address, int flags = 0);
	virtual int receiveFrom(void* buffer, int length, CSocketAddress& address, int flags = 0);
	virtual void sendUrgent(unsigned char data);

	virtual int available();
	virtual bool poll(const CTimeSpan& timeout, int mode);

	virtual void setSendBufferSize(int size);
	virtual int getSendBufferSize();
	virtual void setReceiveBufferSize(int size);
	virtual int getReceiveBufferSize();
	virtual void setSendTimeout(const CTimeSpan& timeout);
	virtual CTimeSpan getSendTimeout();
	virtual void setReceiveTimeout(const CTimeSpan& timeout);
	virtual CTimeSpan getReceiveTimeout();

	virtual CSocketAddress address();
	virtual CSocketAddress peerAddress();
	void setOption(int level, int option, int value);
	void setOption(int level, int option, unsigned value);
	void setOption(int level, int option, unsigned char value);
	void setOption(int level, int option, const CTimeSpan& value);
	void setOption(int level, int option, const CIPAddress& value);
	virtual void setRawOption(int level, int option, const void* value, JH_SOCKETLEN length);

	void getOption(int level, int option, int& value);
	void getOption(int level, int option, unsigned& value);
	void getOption(int level, int option, unsigned char& value);
	void getOption(int level, int option, CTimeSpan& value);
	void getOption(int level, int option, CIPAddress& value);
	virtual void getRawOption(int level, int option, void* value, JH_SOCKETLEN& length);

	void setLinger(bool on, int seconds);
	void getLinger(bool& on, int& seconds);
	void setNoDelay(bool flag);
	bool getNoDelay();
	void setKeepAlive(bool flag);
	bool getKeepAlive();
	void setReuseAddress(bool flag);
	bool getReuseAddress();
	void setReusePort(bool flag);
	bool getReusePort();
	void setOOBInline(bool flag);
	bool getOOBInline();
	void setBroadcast(bool flag);
	bool getBroadcast();
	virtual void setBlocking(bool flag);
	virtual bool getBlocking() const;

	virtual bool secure() const;
	int socketError();
	JH_SOCKET sockfd() const;

	void ioctl(int request, int& arg);
	void ioctl(int request, void* arg);
#if defined(JH_OS_LINUX)
	int fcntl(int request);
	int fcntl(int request, long arg);
#endif

	bool initialized() const;

protected:
	CSocketImpl();
	CSocketImpl(JH_SOCKET sockfd);
	virtual ~CSocketImpl();

	virtual void init(int af);
	void initSocket(int af, int type, int proto = 0);
	void reset(JH_SOCKET fd = JH_INVALID_SOCKET);

	static int lastError();
	static void error();
	static void error(const std::string& arg);
	static void error(int code);
	static void error(int code, const std::string& arg);

private:
	CSocketImpl(const CSocketImpl&);
	CSocketImpl& operator = (const CSocketImpl&);

	JH_SOCKET m_sockfd;

	CTimeSpan m_recvTimeout;
	CTimeSpan m_sndTimeout;
	bool      m_blocking;

	friend class CSocket;
};


NET_NAMESPACE_END

#endif
